package cn.iocoder.yudao.module.product.selection.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.thirdparty.zxhx.ZXHXApiClientFactory;
import cn.iocoder.yudao.framework.thirdparty.zxhx.dto.*;
import cn.iocoder.yudao.framework.thirdparty.zxhx.enums.ZXHXMessageType;
import cn.iocoder.yudao.module.api.supply.enums.SupplyDataSourceEnum;
import cn.iocoder.yudao.module.api.supply.enums.SupplyOperationEnum;
import cn.iocoder.yudao.module.api.supply.enums.SupplySpuStatusEnum;
import cn.iocoder.yudao.module.product.selection.dao.ProductSupplySelectionMapper;
import cn.iocoder.yudao.module.product.selection.dao.ProductSupplySyncLogMapper;
import cn.iocoder.yudao.module.product.selection.pojo.po.ProductSupplySelection;
import cn.iocoder.yudao.module.product.selection.pojo.po.ProductSupplySyncLog;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  这里是文件说明
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/03/04
 */
@Service
@Validated
@Slf4j
public class ZXHXProductSelectionSyncServiceImpl implements ZXHXProductSelectionSyncService {
    @Resource
    private ProductSupplySelectionMapper productSupplySelectionMapper;
    @Resource
    private ProductSupplySyncLogMapper productSupplySyncLogMapper;

    private final Lock lock = new ReentrantLock();
    private int successCount = 0;

    @Override
    public int autoInit(int pageNo, int pageSize) {
        Long count = productSupplySelectionMapper.selectCount();
        if (count != null && count > 0L) {
            log.warn("自营店商品已经初始化过了，需要重新初始化的话请清空选品库表的数据");
            return 0;
        }
        pageNo = Math.max(1, pageNo);
        ZXHXSpuIdListRespDTO spuIdListRespVO = ZXHXApiClientFactory.productClient().getSpuIdList(pageNo, pageSize);
        List<Long> spuIdList = spuIdListRespVO.getSpuIdList();
        if (CollUtil.isEmpty(spuIdList)) {
            log.info("自营店第 {} 页没有商品，每页 {} 个商品", pageNo, pageSize);
            return successCount;
        }
        log.info("自营店共有 {} 个商品，第 {} 页共获取到 {} 个商品", spuIdListRespVO.getTotal(), pageNo, spuIdList.size());
        List<Long> ignoreSpuIds = new ArrayList<>();
        List<Long> successSpuIds = new ArrayList<>();
        List<Long> failureSpuIds = new ArrayList<>();
        for (Long spuId : spuIdListRespVO.getSpuIdList()) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                log.warn("自营店同步商品线程休眠一定的毫秒数出错：{}", e.getMessage());
            }
            // 此处后续可以使用 JDK 21 的虚拟线程代替
            Executors.newCachedThreadPool().execute(() -> {
                ZXHXSpuDetailsRespDTO spuDetailsRespVO = ZXHXApiClientFactory.productClient().getSpuIdByDetails(spuId);
                if (spuDetailsRespVO.getIsOverseas() == 1) {
                    log.warn("自营店忽略海外商品SpuID:{}，商品名称:{}", spuId, spuDetailsRespVO.getStoreName());
                    ignoreSpuIds.add(spuId);
                    return;
                }
                try {
                    if (CollUtil.isEmpty(spuDetailsRespVO.getSku())) {
                        spuDetailsRespVO.setSku(new ArrayList<>());
                        spuDetailsRespVO.setSpecType(0);
                    }
                    syncSpu(spuDetailsRespVO);
                    log.info("自营店新增商品成功，SpuID:{}，商品名称:{}", spuDetailsRespVO.getSpuId(), spuDetailsRespVO.getStoreName());
                    successSpuIds.add(spuId);
                } catch (Exception e) {
                    log.error("自营店同步商品失败，SpuID:{}，商品名称:{} 原因:{}", spuDetailsRespVO.getSpuId(), spuDetailsRespVO.getStoreName(), e.getMessage());
                    failureSpuIds.add(spuId);
                }
            });
        }
        try {
            lock.lock();
            successCount += successSpuIds.size();
        } finally {
            lock.unlock();
        }
        log.info("自营店第 {} 页共忽略 {} 个商品，同步成功 {} 个商品，同步失败 {} 个商品", pageNo, ignoreSpuIds.size(), successSpuIds.size(), failureSpuIds.size());
        ProductSupplySyncLog syncLogDO = new ProductSupplySyncLog();
        syncLogDO.setSource(SupplyDataSourceEnum.ZXHX.getValue());
        syncLogDO.setOperation(SupplyOperationEnum.DELETE.getValue());
        syncLogDO.setOriginSpuIds(spuIdList);
        syncLogDO.setIgnoreSpuIds(ignoreSpuIds);
        syncLogDO.setSuccessSpuIds(successSpuIds);
        syncLogDO.setFailureSpuIds(failureSpuIds);
        productSupplySyncLogMapper.insert(syncLogDO);
        try {
            Thread.sleep(1000);
            return autoInit(pageNo + 1, pageSize);
        } catch (Exception e) {
            log.error("自营店同步第 {} 页商品出错：{}", pageNo, e.getMessage());
        }
        return successCount;
    }

    @Override
    public int autoAdd() throws Exception {
        MessageIds messageIds = pullChangeMessage(ZXHXMessageType.PRODUCT_ADDED);
        if (messageIds == null || CollUtil.isEmpty(messageIds.spuIds)) {
            return 0;
        }
        ProductSupplySyncLog syncLogDO = new ProductSupplySyncLog();
        syncLogDO.setSource(SupplyDataSourceEnum.ZXHX.getValue());
        syncLogDO.setOperation(SupplyOperationEnum.CREATE.getValue());
        syncLogDO.setOriginSpuIds(messageIds.spuIds.stream().toList());
        productSupplySyncLogMapper.insert(syncLogDO);
        log.info("选品库共有 {} 个商品加入", messageIds.spuIds.size());
        int count = syncSpu(syncLogDO, messageIds.spuIds.stream().toList());
        ZXHXApiClientFactory.messageClient().removeMessage(new ZXHXMessageRemoveReqDTO(messageIds.ids.stream().toList()));
        return count;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int autoRemove() {
        MessageIds messageIds = pullChangeMessage(ZXHXMessageType.PRODUCT_REMOVED);
        if (messageIds == null || CollUtil.isEmpty(messageIds.spuIds)) {
            return 0;
        }
        ProductSupplySyncLog syncLogDO = new ProductSupplySyncLog();
        syncLogDO.setSource(SupplyDataSourceEnum.ZXHX.getValue());
        syncLogDO.setOperation(SupplyOperationEnum.DELETE.getValue());
        syncLogDO.setOriginSpuIds(messageIds.spuIds.stream().toList());
        productSupplySyncLogMapper.insert(syncLogDO);
        List<ProductSupplySelection> supplySelections = productSupplySelectionMapper.selectBySpuIds(messageIds.spuIds);
        if (CollUtil.isEmpty(supplySelections)) {
            syncLogDO.setFailureSpuIds(messageIds.spuIds.stream().toList());
            productSupplySyncLogMapper.updateById(syncLogDO);
            return 0;
        }
        supplySelections.forEach(data -> data.setStatus(SupplySpuStatusEnum.REMOVED.getValue()));
        productSupplySelectionMapper.updateById(supplySelections);
        syncLogDO.setSuccessSpuIds(messageIds.spuIds.stream().toList());
        productSupplySyncLogMapper.updateById(syncLogDO);
        log.info("选品库共有 {} 个商品移除", supplySelections.size());
        ZXHXApiClientFactory.messageClient().removeMessage(new ZXHXMessageRemoveReqDTO(messageIds.ids.stream().toList()));
        return supplySelections.size();
    }

    @Override
    public int autoModify() throws Exception {
        List<Integer> ids = new ArrayList<>();
        List<Long> spuIds = new ArrayList<>();
        MessageIds messageIds = pullChangeMessage(ZXHXMessageType.PRODUCT_INFO_CHANGED);
        if (messageIds != null && CollUtil.isNotEmpty(messageIds.spuIds)) {
            ids.addAll(messageIds.ids);
            spuIds.addAll(messageIds.spuIds);
        }
        messageIds = pullChangeMessage(ZXHXMessageType.SPU_STATUS_CHANGED);
        if (messageIds != null && CollUtil.isNotEmpty(messageIds.spuIds)) {
            ids.addAll(messageIds.ids);
            spuIds.addAll(messageIds.spuIds);
        }
        // TODO 库存变动、价格变动、规格项上下架变动暂时先笼统的获取全量产品信息进行同步，后续再优化
        messageIds = pullChangeMessage(ZXHXMessageType.STOCK_CHANGED);
        if (messageIds != null && CollUtil.isNotEmpty(messageIds.spuIds)) {
            ids.addAll(messageIds.ids);
            spuIds.addAll(messageIds.spuIds);
        }
        messageIds = pullChangeMessage(ZXHXMessageType.PRICE_CHANGED);
        if (messageIds != null && CollUtil.isNotEmpty(messageIds.spuIds)) {
            ids.addAll(messageIds.ids);
            spuIds.addAll(messageIds.spuIds);
        }
        messageIds = pullChangeMessage(ZXHXMessageType.SKU_STATUS_CHANGED);
        if (messageIds != null && CollUtil.isNotEmpty(messageIds.spuIds)) {
            ids.addAll(messageIds.ids);
            spuIds.addAll(messageIds.spuIds);
        }
        if (CollUtil.isEmpty(spuIds)) {
            return 0;
        }
        ProductSupplySyncLog syncLogDO = new ProductSupplySyncLog();
        syncLogDO.setSource(SupplyDataSourceEnum.ZXHX.getValue());
        syncLogDO.setOperation(SupplyOperationEnum.UPDATE.getValue());
        syncLogDO.setOriginSpuIds(spuIds);
        productSupplySyncLogMapper.insert(syncLogDO);
        log.info("选品库共有 {} 个商品修改", spuIds.size());
        List<ProductSupplySelection> list = productSupplySelectionMapper.selectBySpuIds(new HashSet<>(spuIds));
        if (CollUtil.isEmpty(list)) {
            syncLogDO.setFailureSpuIds(spuIds);
            productSupplySyncLogMapper.updateById(syncLogDO);
            return 0;
        }
        spuIds = list.stream().map(ProductSupplySelection::getSpuId).collect(Collectors.toList());
        int count = syncSpu(syncLogDO, spuIds);
        ZXHXApiClientFactory.messageClient().removeMessage(new ZXHXMessageRemoveReqDTO(ids));
        return count;
    }

    @Nullable
    private MessageIds pullChangeMessage(ZXHXMessageType type) {
        List<ZXHXMessagePoolRespDTO> messagePoolRespDTOS = ZXHXApiClientFactory.messageClient().getMessagePool(type, 1000);
        if (CollUtil.isEmpty(messagePoolRespDTOS)) {
            log.info("供应链({})没有新的消息", type.getName());
            return null;
        }
        Set<Integer> ids = new HashSet<>();
        Set<Long> spuIds = new HashSet<>();
        for (ZXHXMessagePoolRespDTO messagePoolRespDTO : messagePoolRespDTOS) {
            if (Arrays.asList(ZXHXMessageType.STOCK_CHANGED.getValue(),
                            ZXHXMessageType.PRICE_CHANGED.getValue(),
                            ZXHXMessageType.SKU_STATUS_CHANGED.getValue())
                    .contains(messagePoolRespDTO.getType())) {
                ZXHXMessageSkuRespDTO messageSkuRespDTO = JsonUtils.parseObject(messagePoolRespDTO.getContent(), ZXHXMessageSkuRespDTO.class);
                if (messageSkuRespDTO == null) {
                    log.warn("供应链({})新的消息中没有解析出有效的 sku_id: {}", type.getName(), messagePoolRespDTO.getContent());
                    continue;
                }
                Long spuId = productSupplySelectionMapper.selectSpuIdBySkuId(messageSkuRespDTO.getSkuId());
                if (spuId != null) {
                    ids.add(messagePoolRespDTO.getId());
                    spuIds.add(spuId);
                }
            } else if (Arrays.asList(ZXHXMessageType.PRODUCT_ADDED.getValue(),
                            ZXHXMessageType.PRODUCT_INFO_CHANGED.getValue(),
                            ZXHXMessageType.PRODUCT_REMOVED.getValue(),
                            ZXHXMessageType.SPU_STATUS_CHANGED.getValue())
                    .contains(messagePoolRespDTO.getType())) {
                List<ZXHXMessageSpuRespDTO> messageSpuRespDTOS = JsonUtils.parseArray(messagePoolRespDTO.getContent(), ZXHXMessageSpuRespDTO.class);
                if (CollUtil.isEmpty(messageSpuRespDTOS)) {
                    log.warn("供应链({})新的消息中没有解析出有效的 spu_id: {}", type.getName(), messagePoolRespDTO.getContent());
                    continue;
                }
                ids.add(messagePoolRespDTO.getId());
                spuIds.addAll(messageSpuRespDTOS.stream().map(ZXHXMessageSpuRespDTO::getSpuId).toList());
            }
        }
        if (CollUtil.isEmpty(spuIds)) {
            log.warn("供应链({})新的消息中没有解析出有效的 spu_id", type.getName());
        }
        return new MessageIds(ids, spuIds);
    }

    private record MessageIds(Set<Integer> ids, Set<Long> spuIds) {
    }

    private int syncSpu(ProductSupplySyncLog syncLogDO, List<Long> spuIds) throws InterruptedException {
        List<Long> ignoreSpuIds = new ArrayList<>();
        List<Long> successSpuIds = new ArrayList<>();
        for (Long spuId : spuIds) {
            Thread.sleep(200);
            ZXHXSpuDetailsRespDTO spuDetailsRespVO = ZXHXApiClientFactory.productClient().getSpuIdByDetails(spuId);
            if (spuDetailsRespVO.getIsOverseas() == 1) {
                log.warn("选品库忽略海外商品SpuID:{}，商品名称:{}", spuId, spuDetailsRespVO.getStoreName());
                ignoreSpuIds.add(spuId);
                continue;
            }
            try {
                syncSpu(spuDetailsRespVO);
                log.info("选品库保存商品成功，SpuID:{}，商品名称:{}", spuDetailsRespVO.getSpuId(), spuDetailsRespVO.getStoreName());
                successSpuIds.add(spuId);
            } catch (Exception e) {
                log.error("选品库同步商品失败，SpuID:{}，商品名称:{}", spuDetailsRespVO.getSpuId(), spuDetailsRespVO.getStoreName());
                e.printStackTrace(System.err);
            }
        }
        log.warn("选品库共忽略 {} 个商品：{}", ignoreSpuIds.size(), ignoreSpuIds);
        log.warn("选品库共同步 {} 个商品：{}", successSpuIds.size(), successSpuIds);
        syncLogDO.setSuccessSpuIds(successSpuIds);
        syncLogDO.setIgnoreSpuIds(ignoreSpuIds);
        List<Long> copySpuIds = new ArrayList<>(spuIds);
        copySpuIds.removeAll(successSpuIds);
        copySpuIds.removeAll(ignoreSpuIds);
        syncLogDO.setFailureSpuIds(copySpuIds);
        productSupplySyncLogMapper.updateById(syncLogDO);
        return successSpuIds.size();
    }

    private void syncSpu(ZXHXSpuDetailsRespDTO spu) {
        if (CollUtil.isEmpty(spu.getSku())) {
            spu.setSku(new ArrayList<>());
            spu.setSpecType(0);
        }
        int costPrice = spu.getPrice().intValue() * 100;
        int marketPrice = spu.getMarketPrice().intValue() * 100;
        int stock = spu.getSku().stream().mapToInt(ZXHXSpuDetailsRespDTO.SkuDTO::getStock).sum();
        double discount = NumberUtil.div(costPrice, marketPrice) * 10;
        log.info("选品库商品SpuID:{}，商品名称:{}，成本价:{}，零售价:{}，折扣:{}", spu.getSpuId(), spu.getStoreName(), costPrice, marketPrice, discount);
        ProductSupplySelection selectionDO = new ProductSupplySelection();
        selectionDO.setSource(SupplyDataSourceEnum.ZXHX.getValue());
        selectionDO.setSpuId(spu.getSpuId());
        selectionDO.setSpuName(spu.getStoreName());
        selectionDO.setSpuPicUrl(spu.getImage());
        selectionDO.setSpu3rdLink(spu.getThirdpartyLink());
        selectionDO.setCategoryId(NumberUtil.parseLong(spu.getClassId()));
        selectionDO.setCategoryName(Optional.ofNullable(spu.getCategory()).orElse(new ZXHXSpuDetailsRespDTO.CategoryDTO()).getName());
        selectionDO.setBrandId(spu.getBrandId());
        selectionDO.setBrandName(Optional.ofNullable(spu.getBrand()).orElse(new ZXHXSpuDetailsRespDTO.BrandDTO()).getName());
        selectionDO.setUnitName(spu.getUnitName());
        selectionDO.setMultiSpec(spu.getSpecType() == 1);
        selectionDO.setCostPrice(costPrice);
        selectionDO.setMarketPrice(marketPrice);
        selectionDO.setStock(stock);
        selectionDO.setProfitPercent(spu.getRate());
        selectionDO.setDiscount(discount);
        selectionDO.setSkuIds(spu.getSku().stream().map(ZXHXSpuDetailsRespDTO.SkuDTO::getSkuId).collect(Collectors.toList()));
        selectionDO.setStatus(spu.getStatus() == 1 ? SupplySpuStatusEnum.DISABLED.getValue() : SupplySpuStatusEnum.ENABLED.getValue());
        selectionDO.setSnapshot(JsonUtils.toJsonString(spu));
        selectionDO.setSpuDetails(spu);
        ProductSupplySelection supplySelectionDO = productSupplySelectionMapper.selectBySpuId(spu.getSpuId());
        if (supplySelectionDO != null) {
            selectionDO.setId(supplySelectionDO.getId());
            productSupplySelectionMapper.updateById(selectionDO);
            log.info("选品库更新商品成功，SpuID:{}，商品名称:{}", spu.getSpuId(), spu.getStoreName());
            return;
        }
        productSupplySelectionMapper.insert(selectionDO);
        log.info("选品库插入商品成功，SpuID:{}，商品名称:{}", spu.getSpuId(), spu.getStoreName());
    }

}
